package org.syntaxlisp.core.match.tree;

import org.syntaxlisp.core.element.DefID;
import org.syntaxlisp.core.utils.CommonUtils;
import org.syntaxlisp.core.utils.ListTreeNode;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;

import java.util.ArrayList;
import java.util.List;

/**
 * Pvt
 * @author HuangYijun
 * @data 2023-09-13 17:24
 **/
@Builder
@Data
public class PureCursor {

    private int id;

    private List<RepeatStartFrame> repeatStartStack;

//    private List<RepeatFrame> repeatStack;

    private ListTreeNode<RepeatFrame, Boolean> repeatStackLastNode;

    private MatchState curState;

    private NewMatchSeqNode curMatchNode;

    private int defStart;

    private int curOffset;

    private int nextOffset;

    private ComeFromInfo comeFromInfo;

    public enum ComeFromType {
        GEN_START,
        ROLLBACK_EXPAND,
        ROLLBACK_REF_REDUCE,
        EXPAND,
        REF_REDUCE
    }

    @Builder
    @Getter
    public static class ComeFromInfo {

        private int comeFromCursorId;

        private ComeFromType comeFromType;

        public String toString () {
            return "[" + comeFromType + "," + comeFromCursorId + "]";
        }

    }

    private static final String CRLF = "\r\n";

    private static final String START = "+++++";

    private static final String END = "-----";

    public String toTrace () {
        StringBuilder stringBuilder = new StringBuilder(START);
        stringBuilder.append(id).append(CRLF);
        stringBuilder.append(comeFromInfo).append(CRLF);
        stringBuilder.append(curState.getSrc().getId()).append(CRLF);
        stringBuilder.append(curState.getSrc()).append(CRLF);
        stringBuilder.append(defStart).append(CRLF);
        stringBuilder.append(curOffset).append(CRLF);
        stringBuilder.append(nextOffset).append(CRLF);
/*        stringBuilder.append("cursor id:").append(id).append(CRLF);
        stringBuilder.append("come from info:").append(comeFromInfo).append(CRLF);
        stringBuilder.append("state id:").append(curState.getSrc().getId()).append(CRLF);
        stringBuilder.append("state element:").append(curState.getSrc()).append(CRLF);
        stringBuilder.append("def start:").append(defStart).append(CRLF);
        stringBuilder.append("cur offset:").append(curOffset).append(CRLF);
        stringBuilder.append("next offset:").append(nextOffset).append(CRLF);*/
//        stringBuilder.append(curMatchNode.hashCode()).append(CRLF);
        if (repeatStackLastNode != null) {
            repeatStackLastNode.getEntireKeyList()
                               .forEach(repeatFrame -> stringBuilder.append(repeatFrame.getTrace())
                                                                    .append(CRLF));
        }
        stringBuilder.append(END).append(CRLF);
        return stringBuilder.toString();
    }
/*

    public List<RepeatFrame> getRepeatStack () {
        if (repeatStack == null) {
            repeatStack = new ArrayList<>();
        }
        return repeatStack;
    }
*/

    public List<RepeatStartFrame> getRepeatStartStack () {
        if (repeatStartStack == null) {
            repeatStartStack = new ArrayList<>();
        }
        return repeatStartStack;
    }

/*    public List<RepeatFrame> copyRepeatStack () {
        return repeatStack == null
               ? CommonUtils.asWritableList()
               : CommonUtils.copyStack(repeatStack);
    }*/

    public List<RepeatStartFrame> copyRepeatStartStack () {
        return repeatStartStack == null
               ? CommonUtils.asWritableList()
               : CommonUtils.copyStack(repeatStartStack);
    }

    public boolean isReducing () {
        return nextOffset == NewMatchSeqNode.REDUCING;
    }

    public boolean isSameRepeat(PureCursor cursor) {
        if (repeatStackLastNode != null
                && cursor.getRepeatStackLastNode() != null) {
            return repeatStackLastNode.getNodeKey()
                                      .getUniqueKey()
                                      .equals(cursor.getRepeatStackLastNode()
                                                    .getNodeKey()
                                                    .getUniqueKey());
        } else {
            return false;
        }
    }

    public boolean isRefCursor() {
        return curState.isRef();
    }

    public boolean isReducingRefCursor () {
        return curState.isRef()
                && NewMatchSeqNode.REDUCING == nextOffset;
    }

    public DefID getBelongsToDef() {
        return curState.getBelongsToDef();
    }

    public DefID getRefID() {
        return isRefCursor() ? curState.getDefID() : null;
    }

    public boolean equals (Object otherCursor) {
        if (!(otherCursor instanceof PureCursor)) {
            return false;
        }
        PureCursor ovt = (PureCursor) otherCursor;
        return ovt.getCurOffset() == curOffset
                && ovt.getNextOffset() == nextOffset
                && ovt.getCurState().equals(curState)
                && isSameRepeat(ovt);
    }

    public String toString() {
        return getBelongsToDef() + "," + curOffset + "," + nextOffset;
    }
}
